/**
 * 
 * merge level=20
 * 
 * A Picker field that contains a tree panel on its popup, enabling selection of
 * tree nodes.
 */
Ext.define('expand.ux.field.DataTreePicker', {
	extend : 'Ext.form.field.Picker',
	xtype : 'datatreepicker',

	uses : ['Ext.tree.Panel'],
	requires : ['expand.plugin.TreeNodeDisabled'],

	triggerCls : Ext.baseCSSPrefix + 'form-arrow-trigger',

	config : {
		/**
		 * @cfg {Ext.data.TreeStore} store A tree store that the tree picker will be
		 *      bound to
		 */
		store : null,

		/**
		 * @cfg {String} displayField The field inside the model that will be used
		 *      as the node's text. Defaults to the default value of
		 *      {@link Ext.tree.Panel}'s `displayField` configuration.
		 */
		displayField : null,

		valueField : 'value',

		/**
		 * @cfg {Array} columns An optional array of columns for multi-column trees
		 */
		columns : null,

		/**
		 * @cfg {Boolean} selectOnTab Whether the Tab key should select the
		 *      currently highlighted item. Defaults to `true`.
		 */
		selectOnTab : true,

		/**
		 * @cfg {Number} maxPickerHeight The maximum height of the tree dropdown.
		 *      Defaults to 300.
		 */
		maxPickerHeight : 300,

		/**
		 * @cfg {Number} minPickerHeight The minimum height of the tree dropdown.
		 *      Defaults to 100.
		 */
		minPickerHeight : 10
	},

	editable : false,

	initComponent : function() {
		var me = this;
		me.callParent(arguments);

		me.mon(me.store, {
					scope : me,
					load : me.onLoad,
					update : me.onUpdate
				});
	},

	/**
	 * Creates and returns the tree panel to be used as this field's picker.
	 */
	createPicker : function() {
		var me = this, picker = new Ext.tree.Panel({
					shrinkWrapDock : 2,
					store : me.store,
					floating : true,
					displayField : me.displayField,
					columns : me.columns,
					minHeight : me.minPickerHeight,
					maxHeight : me.maxPickerHeight,
					manageHeight : false,
					shadow : false,
					rootVisible : false,
					border : true,
					autoScroll : true,
					listeners : {
						scope : me,
						itemclick : me.onItemClick
					},
					viewConfig : {
						listeners : {
							scope : me,
							render : me.onViewRender
						}
					},
					plugins : [{
								ptype : 'dvp_nodedisabled'
							}]

				}), view = picker.getView();

		if (Ext.isIE9 && Ext.isStrict) {
			// In IE9 strict mode, the tree view grows by the height of the horizontal
			// scroll bar when the items are highlighted or unhighlighted.
			// Also when items are collapsed or expanded the height of the view is
			// off. Forcing a repaint fixes the problem.
			view.on({
						scope : me,
						highlightitem : me.repaintPickerView,
						unhighlightitem : me.repaintPickerView,
						afteritemexpand : me.repaintPickerView,
						afteritemcollapse : me.repaintPickerView
					});
		}
		return picker;
	},

	onViewRender : function(view) {
		view.getEl().on('keypress', this.onPickerKeypress, this);
	},

	/**
	 * repaints the tree view
	 */
	repaintPickerView : function() {
		var style = this.picker.getView().getEl().dom.style;

		// can't use Element.repaint because it contains a setTimeout, which results
		// in a flicker effect
		style.display = style.display;
	},

	/**
	 * Aligns the picker to the input element
	 */
	alignPicker : function() {
		var me = this, picker;

		if (me.isExpanded) {
			picker = me.getPicker();
			if (me.matchFieldWidth) {
				// Auto the height (it will be constrained by max height)
				picker.setWidth(me.bodyEl.getWidth());
			}
			if (picker.isFloating()) {
				me.doAlign();
			}
		}
	},

	/**
	 * Handles a click even on a tree node
	 * 
	 * @private
	 * @param {Ext.tree.View}
	 *          view
	 * @param {Ext.data.Model}
	 *          record
	 * @param {HTMLElement}
	 *          node
	 * @param {Number}
	 *          rowIndex
	 * @param {Ext.EventObject}
	 *          e
	 */
	onItemClick : function(view, record, node, rowIndex, e) {
		this.selectItem(record);
	},

	/**
	 * Handles a keypress event on the picker element
	 * 
	 * @private
	 * @param {Ext.EventObject}
	 *          e
	 * @param {HTMLElement}
	 *          el
	 */
	onPickerKeypress : function(e, el) {
		var key = e.getKey();

		if (key === e.ENTER || (key === e.TAB && this.selectOnTab)) {
			this.selectItem(this.picker.getSelectionModel().getSelection()[0]);
		}
	},

	/**
	 * Changes the selection to a given record and closes the picker
	 * 
	 * @private
	 * @param {Ext.data.Model}
	 *          record
	 */
	selectItem : function(record) {
		if (record.raw['disabled'])
			return false;
		var me = this;
		me.setValue(record.raw[me.valueField]);
		me.collapse();
		me.picker.hide();
		me.inputEl.focus();
		me.fireEvent('select', me, record);
	},

	/**
	 * Runs when the picker is expanded. Selects the appropriate tree node based
	 * on the value of the input element, and focuses the picker so that keyboard
	 * navigation will work.
	 * 
	 * @private
	 */
	onExpand : function() {
		var me = this, picker = me.picker, store = picker.store, value = me.value, node;
		if (value) {
			node = store.getRoot().findChildBy(function(n) {
						if (n.raw[me.valueField] == value)
							return true;
					}, null, true);
		}
		if (!node) {
			node = store.getRoot();
		}
		picker.getView().getSelectionModel().select(node);

		Ext.defer(function() {
					picker.getView().focus();
				}, 1);
	},

	setValue : function(value) {
		var me = this, oldvalue = me.value, record;
		me.value = value;
		if (me.store.loading) {
			return me;
		}
		if (value === undefined) {
			record = me.store.getRoot();
			if (record.raw)
				me.value = record.raw[me.valueField];
		} else {
			record = me.store.getRoot().findChildBy(function(n) {
						if (n.raw[me.valueField] == value)
							return true;
					}, null, true);
			if (record == null)
				record = me.store.getRoot();
		}
		// set the raw value to the record's display field if a record was found
		me.setRawValue(record && record.get(me.displayField) != 'Root' ? record
				.get(me.displayField) : '');
		// 如果改过了再执行这个，会触发change事件

		if (me.value != oldvalue) {
			me.checkChange();
		}
		return me;
	},

	getSubmitValue : function() {
		return this.value;
	},

	/**
	 * Returns the current data value of the field (the idProperty of the record)
	 * 
	 * @return {Number}
	 */
	getValue : function() {
		return this.value;
	},

	/**
	 * Handles the store's load event.
	 * 
	 * @private
	 */
	onLoad : function() {
		var value = this.value;

		if (value) {
			this.setValue(value);
		}
	},

	onUpdate : function(store, rec, type, modifiedFieldNames) {
		var display = this.displayField;

		if (type === 'edit' && modifiedFieldNames
				&& Ext.Array.contains(modifiedFieldNames, display)
				&& this.value === rec.raw[this.valueField]) {
			this.setRawValue(rec.get(display));
		}
	}

});